package com.easypan.utils.minio;

import com.easypan.utils.minio.config.MinioConfig;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Minio 工具类
 * @author jtz
 */

@Component
@RequiredArgsConstructor
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig minioConfig;


    /**
     * 允许的文件扩展名列表
     */
    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList(".jpg", ".png", ".jpeg",
            ".pdf", ".xls", ".xlsx", ".doc", ".docx", ".ppt", ".pptx",
            ".zip", ".rar", ".7z");

    /**
     * 文件大小限制
     */
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024;

    /**
     * 上传文件
     *
     * @param file 文件对象
     * @return 文件访问 URL 字符串
     */
    public String upload(MultipartFile file) throws Exception {
        // 校验文件
        validateFile(file);

        // 计算文件的 MD5 值
        String fileMD5;
        try {
            fileMD5 = getMd5(file);
        } catch (Exception e) {
            throw new Exception("文件校验失败，请重试");
        }

        // 使用 MD5 作为文件名，避免重复上传同一文件
        String objectName = fileMD5 + getFileExtension(Objects.requireNonNull(file.getOriginalFilename()));

        // 使用 try-with-resources 确保 InputStream 正确关闭
        try (InputStream inputStream = file.getInputStream()) {
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(objectName)
                    .bucket(minioConfig.getBucketName())
                    .contentType(file.getContentType())
                    .stream(inputStream, file.getSize(), -1)
                    .build();

            // 上传文件到 Minio
            minioClient.putObject(objectArgs);

            // 构建文件访问 URL
            return minioConfig.getResponePrefixUrl() + "/" + minioConfig.getBucketName() + "/" + objectName;

        } catch (Exception e) {
            throw new Exception("文件上传失败，请稍后重试");
        }
    }


    /**
     * 删除文件
     *
     * @param fileName 文件名（文件在 minio 中的名字）
     */
    public boolean deleteFile(String fileName) {
        // 检查文件名是否为空
        if (fileName == null || fileName.isEmpty()) {
            return false;
        }

        try {
            RemoveObjectArgs objectArgs = RemoveObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .build();

            minioClient.removeObject(objectArgs);
            return true;
        }
        catch (Exception e) {
            return false;
        }
    }

    /**
     * 校验文件
     *
     * @param file 上传的文件
     */
    private void validateFile(MultipartFile file) throws Exception {
        // 检查文件是否为空
        if (file == null || file.isEmpty()) {
            throw new Exception("上传的文件为空");
        }

        // 获取文件名并检查是否为空
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new Exception("文件名不能为空");
        }

        // 检查文件类型是否合法
        String extension = getFileExtension(originalFilename);
        if (!ALLOWED_EXTENSIONS.contains(extension.toLowerCase())) {
            throw new Exception("不支持的文件类型格式，仅支持: " + ALLOWED_EXTENSIONS);
        }

        // 检查文件大小是否超过限制
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new Exception("文件大小不能超过 " + (MAX_FILE_SIZE / 1024 / 1024) + "MB");
        }
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName 文件名
     * @return 文件扩展名（例如 ".jpg"）
     */
    private String getFileExtension(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
    }

    /**
     * 计算文件的 MD5 值
     *
     * @param file 上传的文件
     * @return 文件的 MD5 值
     */
    private String getMd5(MultipartFile file) throws IOException {
        try (InputStream inputStream = file.getInputStream()) {
            return DigestUtils.md5DigestAsHex(inputStream);
        }
    }


//    /**
//     * 获取文件的 md5 值
//     * @param file 文件对象
//     * @return 返回文件的 md5 值
//     */
//    public String getMd5(MultipartFile file) {
//        try {
//            //获取文件的byte信息
//            byte[] uploadBytes = file.getBytes();
//            // 拿到一个MD5转换器
//            MessageDigest md5 = MessageDigest.getInstance("MD5");
//            byte[] digest = md5.digest(uploadBytes);
//            //转换为16进制
//            return new BigInteger(1, digest).toString(16);
//        } catch (Exception e) {
//            System.out.println(e.getMessage());
//        }
//        return null;
//    }

//    /**
//     *  重命名文件（已废弃）
//     *
//     * @param originalFilename 源文件名
//     * @return
//     */
//    public static String renameFileWithTimestamp(String originalFilename) {
//        // 获取当前时间戳
//        LocalDateTime now = LocalDateTime.now();
//        // 定义时间戳的格式
//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
//        // 格式化当前时间戳
//        String timestamp = now.format(formatter);
//
//        // 获取文件扩展名
//        String fileExtension = "";
//        int lastIndexOfDot = originalFilename.lastIndexOf('.');
//        if (lastIndexOfDot >= 0) {
//            fileExtension = originalFilename.substring(lastIndexOfDot);
//        }
//
//        // 生成新文件名，结合时间戳和原始文件名（如果有的话）
//        return "file_" + timestamp + fileExtension;
//    }


}
